<?php

namespace magein\utils;

/**
 * 树结构
 * Class Tree
 */
class Tree
{
    /**
     * 主键字段
     * @var string
     */
    private string $primary = 'id';

    /**
     * 父级字段
     * @var string
     */
    private string $parent_id = 'parent_id';

    /**
     * @var string
     */
    private string $child = 'child';

    /**
     * @var array
     */
    private mixed $data = [];

    public function __construct($data = null)
    {
        $this->data = $data;
    }

    /**
     * @param array $data
     * @return self
     */
    public static function init(array $data): Tree
    {
        $self = new self();
        $self->data = $data;
        return $self;
    }

    /**
     * @param string $primary
     * @return $this
     */
    public function setPrimary(string $primary): Tree
    {
        $this->primary = $primary ?: $this->primary;

        return $this;
    }

    /**
     * @param string $parent_id
     * @return $this
     */
    public function setParentId(string $parent_id): Tree
    {
        $this->parent_id = $parent_id ?: $this->parent_id;

        return $this;
    }

    public function getChild(): string
    {
        return $this->child;
    }

    /**
     * @param string $child
     * @return $this
     */
    public function setChild(string $child): Tree
    {
        $this->child = $child;

        return $this;
    }


    /**
     * 获取树
     * @param callable|null $callback
     * @return array
     */
    public function transfer(callable $callback = null): array
    {
        if (empty($this->data)) {
            return [];
        }

        $tree = function ($data, $pid = 0) use (&$tree, $callback) {
            $result = [];
            foreach ($data as $item) {
                if (is_callable($callback)) {
                    $item = call_user_func($callback, $item);
                }
                if ($item[$this->parent_id] == $pid) {
                    $children = $tree($data, $item[$this->primary]);
                    if (!empty($children)) {
                        $item[$this->child] = $children;
                    }
                    $result[] = $item;
                }
            }
            return $result;
        };

        return $tree($this->data);
    }

    /**
     * 获取层
     * @param int $limit 限制处理的层级关系
     * @return array
     */
    public function floor(int $limit = 3): array
    {
        if (empty($this->data)) {
            return [];
        }

        $first = current($this->data);
        if (empty($first[$this->child])) {
            $data = $this->transfer();
        } else {
            $data = $this->data;
        }

        $tree = function ($data, $pid = 0, $level = 1) use (&$tree, $limit) {

            static $result = [];

            if (empty($data)) {
                return $result;
            }

            if ($level > $limit) {
                return $result;
            }

            foreach ($data as $val) {

                if ($val[$this->parent_id] == $pid) {

                    $val['__level__'] = $level;

                    $sign = $result[$val[$this->parent_id]] ?? [];
                    if ($sign) {
                        $val['__sign__'] = $sign['__sign__'] . '_' . $val[$this->primary];
                    } else {
                        $val['__sign__'] = $val[$this->parent_id] ? $val[$this->parent_id] . '_' . $val[$this->primary] : $val[$this->primary];
                    }

                    $child = $val[$this->child] ?? [];
                    unset($val[$this->child]);

                    $result[$val['id']] = $val;

                    if ($child) {
                        $tree($child, $val['id'], $level + 1);
                    }
                }
            }
            return $result;
        };

        return $tree($data);
    }
}